require "strscan"

require "lib/time"

module MediaTools
  
  module Cue
    
    module CueReader
      def read_all(io)
        io.each_line do |line|
          @current_line = line
          scanner = StringScanner.new line.chomp
          
          scanner.skip(/\s+/)
          command = scanner.scan(/\w+/)
          next unless command
          
          args = []
          while !scanner.eos?
            scanner.skip(/\s+/)
            if scanner.skip(/"/)
              raise RuntimeError.new("unterminated quotes") unless scanner.scan(/(.*?[^\\"])"/)
              args.push(scanner[1])
            else
              arg = scanner.scan(/\S+/)
              args << arg if arg
            end
          end
          
          command_sym = "on_#{command.downcase}".to_sym
          send(command_sym, *args) if respond_to? command_sym, true
        end
        
      end

      private
      attr :current_line
    end
    
    class PlainTrack
      attr_accessor :number, :datatype
      attr_accessor :title, :songwriter, :performer
      attr_accessor :file, :offset
      
      def initialize(number, datatype)
        @number = number
        @datatype = datatype
      end
    end
        
    class CueParser
      include CueReader
      alias_method :parse, :read_all
      
      attr :tracks
      attr_reader :songwriter, :performer, :title
      
      def initialize
        @tracks = []
      end
      
      def on_file(name, type)
        @filename = name
      end
      
      def on_track(number_str, datatype)
        @current_track = PlainTrack.new(number_str.to_i, datatype)
        @tracks << @current_track
      end
      
      def on_index(number_str, time_str)
        number = number_str.to_i
        time = Time.new.from_s(time_str)
        if (number == 1 && @current_track.offset.nil?) || (number == 0 && time == Time.new)
          @current_track.file = @filename
          @current_track.offset = time
        end
      end
      
      def on_songwriter(name)
        if @current_track
          @current_track.songwriter = name
        else
          @songwriter = name
        end
      end
      
      def on_performer(name)
        if @current_track
          @current_track.performer = name
        else
          @performer = name
        end
      end
      
      def on_title(str)
        if @current_track
          @current_track.title = str
        else
          @title = str
        end
      end

    end
    
    class SingleToMultiFileConverterHandler
      attr_reader :lines
      
      def initialize(files)
        @files = files.reverse
        @lines = []
      end
      
      def index(number_str, time_str)
        number = number_str.to_i
        if number == 1
          file = @files.pop
          raise RuntimeError.new("not enough files to process CUE sheet") unless file
          
          lines << "FILE \"#{file}\" WAVE"
        end
        method_missing :index, *[number_str, time_str]
      end
      
      def file(name, type)
      end
      
      def track(number_str, type)
        lines << []
        method_missing :track, *[number_str, type]
      end
      
      def method_missing(method, *args)
        parts = [method.to_s.upcase]
        args.each do |s|
          if s =~ /\s/
            parts << "\"#{s}\"" 
          else
            parts << s
          end
        end
        lines << "#{parts.join(' ')}"
      end
    end
  end
  
end